/*
 * wireless.c
 *
 * Created: 2013/12/19 14:25:00
 *  Author: alivehex
 */ 
#include <stdio.h>
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
#include <string.h>

#include "requests.h"
#include "iolgt8f0xa.h"
#include "wireless.h"
#include "e2prom.h"
#include "uart.h"
#include "gpio.h"
#include "cbuf.h"

#define DEBUG	0

#if (DEBUG)
# define DBG(...)	kprintf(__VA_ARGS__)
#else
# define DBG(...)	{}
#endif

typedef enum {
	PTX_STATE_IDLE,
	PTX_STATE_TX,
} ptx_state_t;

typedef struct {
	// uint16_t ubrr;
	// int parity;
	// int stopBits;
	// int dataBits;
	int rfChannel;
	int rfDataRate;
	int rfPwr;
	int sysRole;
	int sum;
} sys_parameters_t;

#define LOST_PRX_COUNT_MAX		0x1FFFF

#define LED_ON()	_CLR_IO(LED_PORT, LED_PIN)
#define LED_OFF()	_SET_IO(LED_PORT, LED_PIN)
#define LED_BLINK()	_PORT_(LED_PORT) ^= LED_PIN;

#define PTX_UTX_CACHE_SIZEB		64
#define PTX_URX_CACHE_SIZEB		64

static char PTX_UTX_BUF[PTX_UTX_CACHE_SIZEB];
static char PTX_URX_BUF[PTX_URX_CACHE_SIZEB];

// static int PTX_RESP_LENB = 0;
cbuf_t ptxUTxBuffer;
cbuf_t ptxURxBuffer;

static volatile ptx_state_t PTX_STATE = PTX_STATE_IDLE;
//static rf_ctrl_package_t RF_CTRL_PKG;

uint8_t PTX_STATUS = 0;
/* Control package header */
const static char RF_HEADER[4] = {'R','F','?','!'};

// static uint8_t RF_CHANNEL = RF_DEFAULT_CHANNEL;
static volatile sys_role_t RF_ROLE = SYS_ROLE_PTX;

/* 1 = PTX, 0 = PRX */
// #define GET_SYSTEM_ROLE()
//	RF_ROLE = _GET_IO(ROLE_PORT, ROLE_PIN) ? SYS_ROLE_PTX : SYS_ROLE_PRX;
	
// static void ptxWirelessPoll(void);
static void prxWirelessPoll(void);	
// static void loadFromE2prom(sys_parameters_t *ptr);
// static void store2E2prom(sys_parameters_t *ptr);

extern int PRX_package_handler(const char *buffer, int len);


/* Basic SPI read and write function */
static uint8_t spiReadWriteSW(uint8_t data) {
	uint8_t buf = 0;
	uint8_t i;

	for(i = 0; i < 8; i++) {
		if(data & 0x80) { // MSB
			_SET_IO(MOSI_PORT, MOSI_PIN);
		} else {
			_CLR_IO(MOSI_PORT, MOSI_PIN);
		}
		
		_SET_IO(SPCK_PORT, SPCK_PIN);
		
		data <<= 1;
		// receive data
		buf <<= 1;
		if(_GET_IO(MISO_PORT, MISO_PIN)) {
			buf |= 1;
		}
		
		_CLR_IO(SPCK_PORT, SPCK_PIN);
	}
	return buf;
}

/* nRF24L01 read and write function with command */
static int wirelessRW(uint8_t command, const char *writeBuffer, char *readBuffer, int bufferLengthByte) {
	uint8_t status;
	uint8_t spiData;
	
	_CLR_IO(NPCS_PORT, NPCS_PIN);
	
	status = spiReadWriteSW(command);
	while (bufferLengthByte --) {
		if(writeBuffer) {
			spiData = spiReadWriteSW(*writeBuffer ++);
		} else {
			spiData = spiReadWriteSW(0xFF);
		}
		if(readBuffer) {
			*readBuffer ++ = spiData;
		}
	}
	
	_SET_IO(NPCS_PORT, NPCS_PIN);
	return status;
}

/* Read one byte from nRF24L01 register */
void wirelessWriteReg(uint8_t address, uint8_t value) {
	 wirelessRW(address | 0x20, (const char *)&value, NULL, 1);
}

/* Write one byte to nRF24L01 register */
uint8_t wirelessReadReg(uint8_t address) {
	char buffer;
	
	wirelessRW(address, NULL, &buffer, 1);
	return buffer;
}

/* Write data to WFIFO and enable transfer */
void wirelessWrite(const char *buffer, int bufferLength) {
	while(PTX_STATE == PTX_STATE_TX);
	
	_CLR_IO(CE_PORT, CE_PIN);
	wirelessRW(0xA0, buffer, NULL, bufferLength);
	PTX_STATE = PTX_STATE_TX;
	
	_SET_IO(CE_PORT, CE_PIN);
	_delay_us(50);
	_CLR_IO(CE_PORT, CE_PIN);
}

// 0 = everything is fine
// 1 = timeout
// 2 = no response from the remote device
int wirelessWriteSync(const char *buffer, int len) {
	volatile unsigned int timeout_us = 800;
	
	_CLR_IO(CE_PORT, CE_PIN);
#if (0)
	// flush RX-FIFO
	wirelessRW(0xE2, NULL, NULL, 0);
	// flush TX-FIFO
	wirelessRW(0xE1, NULL, NULL, 0);
#endif
	wirelessRW(0xA0, buffer, NULL, len);	
	PTX_STATE = PTX_STATE_TX;
	
	_SET_IO(CE_PORT, CE_PIN);
	_delay_us(50);
	_CLR_IO(CE_PORT, CE_PIN);
	
	do {
		if (PTX_STATE != PTX_STATE_TX) {
			break;
		}
		_delay_ms(1);
	} while (-- timeout_us);
	
	if (timeout_us == 0) {
		return 1;
	}
	
	if (PTX_STATUS & PTX_STATUS_MAXRT) {
		return 2;
	}
	
	return 0;
}

int wirelessEnableTransmit() {
	volatile unsigned long timeout_us = 50000;
	
	PTX_STATE = PTX_STATE_TX;
	
	_SET_IO(CE_PORT, CE_PIN);
	_delay_us(50);
	_CLR_IO(CE_PORT, CE_PIN);
	
	do {
		if (PTX_STATE != PTX_STATE_TX) {
			break;
		}
		_delay_us(1);
	} while (-- timeout_us);
	
	if (timeout_us == 0) {
		return 1;
	}
	
	if (PTX_STATUS & PTX_STATUS_MAXRT) {
		return 2;
	}
	
	return 0;
}

typedef struct {
	uint8_t request;
	uint8_t addrLsb;
	uint8_t addrMsb;
	uint8_t mark;
	uint8_t value0;
	uint8_t datlen;
	uint8_t data[26];
} prx_package_t;

typedef enum {
	PRX_IO8_BIT = 0,
	PRX_IO8_RW,
	PRX_MEM8_BIT,
	PRX_MEM8_RW,
	PRX_RF_BIT,
	PRX_RF_RW,
	PRX_WFLASH,
	PRX_RFLASH,
	PRX_RESET,
	PRX_ECHO,
} prx_request_t;

void prxPackagesHandler(const char *buffer, int len, char *wbuf, int *wbufLen) {
	prx_package_t *pkg = (void *)buffer;
	unsigned char temp;
	
	switch(pkg->request) {
		case PRX_IO8_BIT:
			if (pkg->mark) {
				_SFR_IO8(pkg->addrLsb) |= pkg->value0;
			} else {
				_SFR_IO8(pkg->addrLsb) &= ~pkg->value0;
			}
			wbuf[0] = _SFR_IO8(pkg->addrLsb);
			*wbufLen = 1;
			return;
		case PRX_IO8_RW:
			if (pkg->mark) {
				// write register
				_SFR_IO8(pkg->addrLsb) = pkg->value0;
			}
			wbuf[0] = _SFR_IO8(pkg->addrLsb);
			*wbufLen = 1;
			return;
		case PRX_RF_BIT:
			// CE is already low at this moment
			temp = wirelessReadReg(pkg->addrLsb);
			if (pkg->mark) {
				wirelessWriteReg(pkg->addrLsb, temp | pkg->value0);
			} else {
				wirelessWriteReg(pkg->addrLsb, temp &= ~pkg->value0);
			}
			wbuf[0] = wirelessReadReg(pkg->addrLsb);
			*wbufLen = 1;
			return;
		case PRX_MEM8_RW:
			if (pkg->mark) {
				// write register
				_SFR_MEM8(pkg->addrLsb) = pkg->value0;
			}
			wbuf[0] = _SFR_MEM8(pkg->addrLsb);
			*wbufLen = 1;
			return;		
		case PRX_MEM8_BIT:
			if (pkg->mark) {
				_SFR_MEM8(pkg->addrLsb) |= pkg->value0;
				} else {
				_SFR_MEM8(pkg->addrLsb) &= ~pkg->value0;
			}
			wbuf[0] = _SFR_MEM8(pkg->addrLsb);
			*wbufLen = 1;
			return;	
		case PRX_RF_RW:
			if (pkg->mark) {
				wirelessWriteReg(pkg->addrLsb, pkg->value0);
			}
			wbuf[0] = wirelessReadReg(pkg->addrLsb);
			*wbufLen = 1;
			return;
		case PRX_RESET:
			if ( (pkg->addrMsb == 0x85) && (pkg->addrLsb == 0x79) ) {
				wdt_enable(WDTO_15MS);
				while (1);
			}
			*wbufLen = 0;
			return;
		case PRX_ECHO:
			*wbufLen = 0;
			return;
		default:
			*wbufLen = 0;
			return;
	}
}


void wirelessPoll(void) {
	//if(RF_ROLE == SYS_ROLE_PTX)
	//	ptxWirelessPoll();
	//else
	prxWirelessPoll();
}

/* Write data to nRF24L01 and enable UART TX-INT if there is any data in ptxURxBuffer */	 
#if (0)
static void ptxWirelessPoll(void) {
	char buffer[RF_CACHE_SIZEB];
	int nByte;
	
	if(PTX_STATE == PTX_STATE_IDLE) {
		// check the UART channel first
		nByte = CBufRead(&ptxUTxBuffer, buffer, sizeof(buffer));	
		if(nByte) {
			// write data to nRF24L01
			DBG("PTX WRF %dB\r\n", nByte);
			wirelessWrite(buffer, nByte);
		} else {
			// write an ECHO package to PRX to get data back in ACK package
			wirelessWrite(RF_HEADER, sizeof(RF_HEADER));
		}
	} 

#ifndef ENABLE_USB	
	// enable the RDE interrupt
	if((UCSR0B & (1 << 5)) == 0) {
		if(getDataCount(&ptxURxBuffer)) {
			UCSR0B |= (1 << 5);
		}
	}
#endif
}
#endif

/* enable the UDR interrupt when there is data */
static void prxWirelessPoll(void) {	
	//if((UCSR0B & (1 << 5)) == 0) {
	//	if(getDataCount(&ptxURxBuffer)) {
	//		UCSR0B |= (1 << 5);
	//	}
	//}
	// commands handle in interrupt
}


// void PTXDeviceInit(uint8_t rfChannel) {
void PTXDeviceInit(sys_parameters_t *sys, int msg) {
	char buffer[1];
	int retryLimit = 10;
	
	// stop here if nRF24L01 module is missing
	while(-- retryLimit) {
		// enable the device and enter PTX mode
		wirelessWriteReg(0, (1 << 3) | (1 << 1));
		
		if(wirelessReadReg(0) == ((1 << 3) | (1 << 1))) {
			// kprintf("PTX");
			// throwMsgOnAllBaudrate("PTX", baudrate);
			if(msg)
				kprintf("PTX");
			break;
		} else {
			// kprintf("PTX Wait");
			_delay_ms(50);
		}
	}
	if(retryLimit == 0) {
		// throwMsgOnAllBaudrate("ERR", baudrate);
		kprintf("PTX ERR");
		while(1);
	}
	
	// flush the TX-FIFO
	wirelessRW(0xE1, NULL, NULL, 0);
	// clear all interrupt bits
	wirelessWriteReg(0x7, 0xFF);
	// set RF-channel
	wirelessWriteReg(0x5, sys->rfChannel);
	// set RF-DR, 0dbm and 
	wirelessWriteReg(0x6, (sys->rfDataRate << 3) | (sys->rfPwr << 1) | (1 << 0));
	// to enable the ACK-payload data more then 15B
	wirelessWriteReg(0x4, (3 << 4) | (15 << 0));
	// enable the DYNPD and FEATURE register
	buffer[0] = 0x73;
	wirelessRW(0x50, buffer, NULL, 1);
	// enable the R_RX_PL_WID, W_ACK_PAYLOAD, disable W_TX_PAYLOAD_NOACK
	wirelessWriteReg(0x1D, (1 << 2) | (1 << 1));
	// wirelessWriteReg(0x1D, (1 << 1));
	// DBG("PTX address(0x1D)=0x%x\r\n", wirelessReadReg(0x1D));
	// enable the dynamic package length in all pipes
	wirelessWriteReg(0x1C, 1);
	// DBG("PTX address(0x1C)=0x%x\r\n", wirelessReadReg(0x1C));
	
	_delay_ms(20);
}

// void PRXDeviceInit(uint8_t channel, uint8_t rfDR) {
void PRXDeviceInit(sys_parameters_t *sys, int msg) {
	char buffer[1];
	int retryLimit = 10;
		
	_delay_ms(10);

	_CLR_IO(CE_PORT, CE_PIN);
	// stop here if nRF24L01 module is missing
	while(-- retryLimit) {
		// enable the device and enter PRX mode
		wirelessWriteReg(0, (1 << 3) | (1 << 1) | (1 << 0));
		
		if(wirelessReadReg(0) == ((1 << 3) | (1 << 1) | (1 << 0))) {
			// kprintf("PRX");
			// throwMsgOnAllBaudrate("PRX", baudrate);
			if(msg)
				kprintf("PRX");
			break;
		} else {
			_delay_ms(50);
		}
	}	
	if(retryLimit == 0) {
		// throwMsgOnAllBaudrate("ERR", baudrate);
		kprintf("PRX ERR");
		while(1);
	}
	// flush the RX-FIFO
	wirelessRW(0xE2, NULL, NULL, 0);
	// clear all interrupt bits
	wirelessWriteReg(0x7, 0xFF);
	// set RF-channel
	wirelessWriteReg(0x5, sys->rfChannel);
	// set RF-DR, 0dbm and LNA gain
	wirelessWriteReg(0x6, (sys->rfDataRate << 3) | (sys->rfPwr << 1) | (1 << 0));
	// to enable the ACK-payload data more then 15B
	wirelessWriteReg(0x4, (3 << 4) | (15 << 0));
	// enable the DYNPD and FEATURE register
	buffer[0] = 0x73;
	wirelessRW(0x50, buffer, NULL, 1);
	// enable the R_RX_PL_WID, W_ACK_PAYLOAD, disable W_TX_PAYLOAD_NOACK
	wirelessWriteReg(0x1D, (1 << 2) | (1 << 1));
	// wirelessWriteReg(0x1D, (1 << 1));
	// DBG("PTX address(0x1D)=0x%x\r\n", wirelessReadReg(0x1D));
	// enable the dynamic package length in all pipes
	wirelessWriteReg(0x1C, 1);
	// DBG("PTX address(0x1C)=0x%x\r\n", wirelessReadReg(0x1C));
		
	_delay_ms(20);
		
	_SET_IO(CE_PORT, CE_PIN);
}

int wirelessInit(void) {	
	// Config the GPIOs
	// unsigned long baudrate;
	// uint8_t rfChannel;
	sys_parameters_t sys;
	WIRELESS_LOWLEVEL_INIT();
	
	_CONFIG_IO_INPUT_PULLUP(ROLE_MODE_PORT, ROLE_MODE_PIN);

	sys.rfChannel = RF_DEFAULT_CHANNEL;
	sys.sysRole = SYS_ROLE_PTX;
	sys.rfDataRate = 0;
	sys.rfPwr = 3; // Maxim

	// baudrate = UART_DEFAULT_BAUDRATE; // getSystemBaudrate();
	// rfChannel = 0;	//getSystemRFChannel();
	// GET_SYSTEM_ROLE();
	RF_ROLE = sys.sysRole;
	
	// 0 = PTX, 1 = PRX
	if (_GET_IO2(ROLE_MODE_PORT, ROLE_MODE_PIN)) {
		RF_ROLE = SYS_ROLE_PTX;
	} else {
		RF_ROLE = SYS_ROLE_PRX;
	}
	
	// uartOpen(baudrate, 8, 1, 0);
	// uart1_open(UART_DEFAULT_BAUDRATE);
	// uart1_open2(sys.ubrr, sys.dataBits, sys.stopBits, sys.parity);
	// kprintf("ROLE=%d\r\n", RF_ROLE);
	
	EIMSK = (1 << 2);

	CBufInit(&ptxUTxBuffer, PTX_UTX_BUF, PTX_UTX_CACHE_SIZEB);
	CBufInit(&ptxURxBuffer, PTX_URX_BUF, PTX_URX_CACHE_SIZEB);
	
	// Disable CE to write nRF24L01 register
	_CLR_IO(CE_PORT, CE_PIN);
	
	if(RF_ROLE == SYS_ROLE_PTX) {
		PTXDeviceInit(&sys, 1);
	} else {
		PRXDeviceInit(&sys, 1);
	}
	
	// enable global interrupt
	// sei();
	// do this in Main function
	return RF_ROLE;
}

// static unsigned long RCNT = 0;
//#if (HW_SOP24)
ISR(INT2_vect) {
//#elif (HW_SOP24_HM)
//ISR(INT0_vect) {
//#endif
	uint8_t status;
	uint8_t dataCount;
	char buffer[RF_CACHE_SIZEB];
	char buffer2[RF_CACHE_SIZEB];
	static uint8_t ledBlinkCount = 0;
	// char ret;
	int nbytes;
	// int i;
	// int pipeIndex;
	
	_CLR_IO(CE_PORT, CE_PIN);
	
	status = wirelessRW(0xFF, NULL, NULL, 0);
	wirelessWriteReg(0x7, 0xFF);
	
	// kprintf("INT(0x%x)", status);
	// Re-enable the global interrupt
	// very important because without this opt,
	// TXI and RXI will not have enough time to do their job
	
	// wirelessRW(0x11, NULL, (char *)&dataCount, 1);
	// kprintf("p0:%d", dataCount);
	sei();
	
	// kprintf("S[0x%x]", status);
	// wirelessRW(0x60, NULL, (char *)&dataCount, 1);
	// kprintf("FC[%d]", dataCount);
	
	if(status & (1 << 0)) {
		DBG("[TX_FULL]");
		// nothing to do here
	}
	if( ((status >> 1) & 7) < 6 ) {
		// pipeIndex = (status >> 1) & 7;
		// DBG("[RX_P_NO=%d]", pipeIndex);
		// pipeIndex is reserved for future use
	}
	if( status & (1 << 4) ) { 
		DBG("[MAX_RT]");
		if(RF_ROLE == SYS_ROLE_PTX) {
			// flush the TX FIFO
			// Max retry interrupt, this means PRX is missing
			wirelessRW(0xE1, NULL, NULL, 0);
			PTX_STATE = PTX_STATE_IDLE;
			PTX_STATUS |= PTX_STATUS_MAXRT;
			
			// blink the LED fast
			ledBlinkCount = (ledBlinkCount + 1) % 80;
			switch(ledBlinkCount) {
			case 0: LED_ON();  break;
			case 5:	LED_OFF(); break;
			}
			DBG("[MAX_RT STATE=%d]", PTX_STATE);
		} else {
			// nothing for PRX here
		}
	}
	if( status & (1 << 5) ) {
		DBG("[TX_DS]");
		if(RF_ROLE == SYS_ROLE_PTX) {
			// If there is no data in PRX ACK payload,
			// Data Sent interrupt is asserted.
			PTX_STATE = PTX_STATE_IDLE;
			DBG("[TX_DS STATE=%d]", PTX_STATE);
			PTX_STATUS &= ~PTX_STATUS_MAXRT;
			LED_ON();
		} else {
			// nothing for PRX here
		}
	}
	if( status & (1 << 6) ) {
		DBG("[RXDR]");
		wirelessRW(0x60, NULL, (char *)&dataCount, 1);
		wirelessRW(0x61, NULL, buffer, dataCount);			
		
		if(RF_ROLE == SYS_ROLE_PTX) {
			// ACK data from the PRX device
			// write data to cache for TXE_READ
			// kprintf("RCV %d 0x%x\r\n", dataCount, buffer[0]);			
			CBufWrite(&ptxURxBuffer, buffer, dataCount);
			// enable the TXE interrupt
			// UCSR0B |= (1 << 5);
			
			PTX_STATE = PTX_STATE_IDLE;
			PTX_STATUS &= ~PTX_STATUS_MAXRT;
			LED_ON();
		} else if(RF_ROLE == SYS_ROLE_PRX) {			
			prxPackagesHandler(buffer, dataCount, buffer2, &nbytes);
			if (nbytes) {
				wirelessRW(0xA8, buffer2, NULL, nbytes);
			}
			// write ACK payload data
			// dataCount = CBufRead(&ptxUTxBuffer, buffer, sizeof(buffer));
			// if(dataCount) {
				// RCNT += dataCount;
				// kprintf("%lu\r\n", RCNT);
				// wirelessRW(0xA8, buffer, NULL, dataCount);
			//}
		} else {
			cli();
			while (1) {
				kprintf("PTRX ERR\r\n");
				_delay_ms(1000);
			}
		}
	}
	
	if(RF_ROLE == SYS_ROLE_PRX) {
		// listen packages in the air
		_SET_IO(CE_PORT, CE_PIN);
	}
	DBG("\r\n");
}

ISR(USART0_RX_vect) {
	// char data = UDR0;
	
	// CBufWrite(&ptxUTxBuffer, &data, 1);
}

ISR(USART0_UDRE_vect) {
#if (0)
	char data[1];
	
	if(CBufRead(&ptxURxBuffer, data, 1)) {
		UDR0 = data[0];
	} else {
		//disable the UDRE interrupt
		UCSR0B &= ~(1 << 5);
	}
#endif
}

#if (0)
static void restoreDefaultParameter(sys_parameters_t *ptr) {
	//ptr->ubrr = BDR2UBRR(UART_DEFAULT_BAUDRATE);
	//ptr->dataBits = 8;
	//ptr->stopBits = 1;
	//ptr->parity = 0;
	ptr->rfChannel = RF_DEFAULT_CHANNEL;
	ptr->sysRole = SYS_ROLE_PTX;
	ptr->rfDataRate = 0;
	ptr->rfPwr = 3; // MAX
	//store2E2prom(ptr);
}
#endif

#if (0)
static void loadFromE2prom(sys_parameters_t *ptr) {
	//int sum;
	//int i;
	//char *buffer = (char *)ptr;	
	
	// e2promRead(0, (char *)ptr, sizeof(sys_parameters_t));
	//kprintf("R:");
	//for(i = 0; i < sizeof(sys_parameters_t); i ++) {
		//kprintf("0x%x ", buffer[i]);
	//}
	//kprintf("\r\n");
	
	//sum = ptr->ubrr + ptr->parity + ptr->stopBits +
	//	ptr->dataBits + ptr->rfChannel + ptr->sysRole;
	//if(sum != ptr->sum) {
	restoreDefaultParameter(ptr);
	//}	
}
#endif

#if (0)
static void store2E2prom(sys_parameters_t *ptr) {
	//int i;
	//char *buffer = (char *)ptr;
	
	ptr->sum = ptr->ubrr + ptr->parity + ptr->stopBits + 
		ptr->dataBits + ptr->rfChannel + ptr->sysRole;
	
	e2promWrite(0, (const char *)ptr, sizeof(sys_parameters_t));
	//kprintf("W:");
	//for(i = 0; i < sizeof(sys_parameters_t); i ++) {
		//kprintf("0x%x ", buffer[i]);
	//}
	//kprintf("\r\n");
}
#endif